﻿//  Copyright (c) 2010 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.Mvc;
using System.Globalization;
using System.Text;
using System.IO;

namespace DNA.Mvc
{
    /// <summary>
    /// Represents a MVC component base class that responsible for render the html to ViewContent
    /// </summary>
    /// <remarks>A MVC Component maybe composed by many html elements,it define properties, data and render the html elements to ViewContent.</remarks>
    public abstract class ViewComponent
    {
        private string _id;

        public IViewDataContainer ViewDataContainer { get; internal set; }

        public ViewContext ViewContext { get; internal set; }

        public ViewDataDictionary ViewData { get { return ViewContext.ViewData; } }

        public string Name { get; set; }
        
        public int Width { get; set; }
        
        public int Height { get; set; }

        public virtual string TagName { get { return "div"; } }

        private Dictionary<string, object> htmlAttributes;

        public Dictionary<string, object> HtmlAttributes
        {
            get
            {
                if (htmlAttributes == null)
                    htmlAttributes = new Dictionary<string, object>();
                return htmlAttributes;
            }
        }

        public event ViewComponentRenderDelegate BeforeComponentRender;
        public event ViewComponentRenderDelegate BeforeContentRender;
        public event ViewComponentRenderDelegate AfterContentRender;
        public event ViewComponentRenderDelegate AfterComponentRender;

        public virtual void Render(HtmlTextWriter writer)
        {
            if (string.IsNullOrEmpty(TagName))
                throw new ArgumentNullException("TagName");

            //if (_OnRenderBeginTag != null) _OnRenderBeginTag.Invoke(writer);
            if (BeforeComponentRender != null) BeforeComponentRender(writer);
            RenderBeginTag(writer);

            //if (_OnRenderContent != null) _OnRenderContent.Invoke(writer);
            if (BeforeContentRender != null) BeforeContentRender(writer);
            RenderContent(writer);
            if (AfterContentRender != null) AfterContentRender(writer);

            RenderEndTag(writer);
            if (AfterComponentRender!=null) AfterComponentRender(writer);

            //if (_OnRenderEndTag != null) _OnRenderEndTag.Invoke(writer);
        }

        public virtual void RenderContent(HtmlTextWriter writer) { }

        public string Id
        {
            get
            {
                if ((string.IsNullOrEmpty(_id)) && (!string.IsNullOrEmpty(Name)))
                {
                    var _tag = new TagBuilder(TagName);
                    _tag.GenerateId(Name);
                    if (_tag.Attributes.ContainsKey("id"))
                        _id = _tag.Attributes["id"];
                    else
                        _id = Name;
                }
                return _id;
            }
            private set { _id = value; }
        }

        public void MergeAttribute(string key, string value)
        {
            this.MergeAttribute(key, value, false);
        }

        public void MergeAttribute(string key, string value, bool replaceExisting)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key");
            }

            if (replaceExisting || !this.HtmlAttributes.ContainsKey(key))
                this.HtmlAttributes[key] = value;
            //else
            // this.HtmlAttributes.Add(key, value);
        }

        public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes)
        {
            this.MergeAttributes<TKey, TValue>(attributes, false);
        }

        public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, bool replaceExisting)
        {
            if (attributes != null)
            {
                foreach (KeyValuePair<TKey, TValue> pair in attributes)
                {
                    string key = Convert.ToString(pair.Key, CultureInfo.InvariantCulture);
                    string str2 = Convert.ToString(pair.Value, CultureInfo.InvariantCulture);
                    this.MergeAttribute(key, str2, replaceExisting);
                }
            }
        }

        public virtual void RenderBeginTag(HtmlTextWriter writer)
        {
            TagBuilder _tag = new TagBuilder(TagName);
            
            if (!string.IsNullOrEmpty(Name))
            {
                _tag.GenerateId(Name);
                if (!_tag.Attributes.ContainsKey("id"))
                    _tag.MergeAttribute("id", Name);

                Id = _tag.Attributes["id"];

                if (TagName.Equals("input", StringComparison.OrdinalIgnoreCase) || TagName.Equals("textarea", StringComparison.OrdinalIgnoreCase))
                    _tag.MergeAttribute("name", Name);
            }

            if (Width > 0 || Height > 0)
            {
                string _style = (Width > 0 ? "width:" + Width.ToString() + "px;" : "") + (Height > 0 ? "height:"+Height.ToString() + "px;" : "");
                MergeAttribute("style", _style);
            }

            if (this.HtmlAttributes != null) 
                _tag.MergeAttributes<string, object>(HtmlAttributes);

            writer.Write(_tag.ToString(TagRenderMode.StartTag));
        }

        public virtual void RenderEndTag(HtmlTextWriter writer)
        {
            writer.WriteEndTag(TagName);

        }

        public virtual string GetHtml()
        {
            var result = new StringBuilder();
            using (var writer =new HtmlTextWriter(new StringWriter(result)))
            {
                Render(writer);
            }   
            return result.ToString();
        }
    }

    public delegate void ViewComponentRenderDelegate(HtmlTextWriter writer);
}